package com.edfu.core.commons.service.impl;

import static com.edfu.core.commons.exceptions.CommonExceptions.CATEGORY_NAME_NOT_PRESENT;
import static com.edfu.core.commons.exceptions.CommonExceptions.CATEGORY_NOT_FOUND;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_ID_INVALID;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_NAME_NOT_PRESENT;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_NOT_SAVED_IN_JCR;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_NULL;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_PARENT_ID_INVALID;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_PARENT_TYPE_INVALID;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_STREAM_NULL;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_TYPE_NOT_PRESENT;
import static com.edfu.core.testmgt.exceptions.TestExceptions.RESOURCE_UUID_NOT_PRESENT;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.edfu.core.commons.beans.EduObjectType;
import com.edfu.core.commons.exceptions.CategoryException;
import com.edfu.core.commons.exceptions.InvalidArgumentException;
import com.edfu.core.commons.exceptions.ResourceException;
import com.edfu.core.commons.persistence.api.ICategoryMgtPersistence;
import com.edfu.core.commons.persistence.api.IResourceMgtPersistence;
import com.edfu.core.commons.persistence.entity.CategoryEntity;
import com.edfu.core.commons.persistence.entity.ResourceEntity;
import com.edfu.core.commons.persistence.entity.ResourceMappingEntity;
import com.edfu.core.commons.service.api.INotificationService;
import com.edfu.core.commons.service.api.IResourceMgtService;
import com.edfu.core.commons.util.ResourceMgtBeanTransformer;
import com.edfu.core.contentmgt.jcr.service.api.IContentMgtService;
import com.edfu.core.contentmgt.jcr.vo.Content;
import com.edfu.core.testmgt.vo.Resource;

public class ResourceMgtServiceImpl implements IResourceMgtService {

	private static Logger logger = LoggerFactory
			.getLogger(ResourceMgtServiceImpl.class);

	@Autowired
	private IContentMgtService contentMgtService;

	@Autowired
	private IResourceMgtPersistence resourceMgtPersistence;
	
	@Autowired
	private ICategoryMgtPersistence categoryMgtPersistence;
	
	@Autowired
	private INotificationService notificationService;

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { InvalidArgumentException.class, ResourceException.class })
	public void uploadResource(Resource resource) {
		if (resource == null) {
			logger.warn("Resource is Null");
			throw new InvalidArgumentException(RESOURCE_NULL);
		}
		if (StringUtils.isBlank(resource.getName())
				|| StringUtils.isEmpty(resource.getName())) {
			logger.warn("Resouce name is not present");
			throw new InvalidArgumentException(RESOURCE_NAME_NOT_PRESENT);
		}
		if (StringUtils.isBlank(resource.getType())
				|| StringUtils.isEmpty(resource.getType())) {
			logger.warn("Resouce type is not present");
			throw new InvalidArgumentException(RESOURCE_TYPE_NOT_PRESENT);
		}
		if (resource.getInputStream() == null) {
			logger.warn("Resource Stream is Null");
			throw new InvalidArgumentException(RESOURCE_STREAM_NULL);
		}
		
		if (resource.getCategory() == null || StringUtils.isBlank(resource.getCategory().getName()) || StringUtils.isEmpty(resource.getCategory().getName())) {
			logger.warn("Category name not present");
			throw new InvalidArgumentException(CATEGORY_NAME_NOT_PRESENT);
		}

		Content content = contentMgtService.addFile(resource.getName(),
				resource.getInputStream());
		if (content == null) {
			logger.warn("Resouce not saved in JCR Repo");
			throw new ResourceException(RESOURCE_NOT_SAVED_IN_JCR);
		}
		ResourceEntity re = ResourceMgtBeanTransformer.transformResource(resource);
		re.setCategory(getCategoryEntity(resource.getCategory().getName()));
		re.setUuid(content.getContentUUID());

		ResourceEntity uploadedResource = resourceMgtPersistence.addResource(re);
		notificationService.addInterestsNotification(uploadedResource.getId(), EduObjectType.RESOURCE, resource.getCategory());
	}
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { InvalidArgumentException.class })
	public void deleteResource(String uuid) {
		if (StringUtils.isBlank(uuid) || StringUtils.isEmpty(uuid)) {
			logger.warn("Resource UUID is not present");
			throw new InvalidArgumentException(RESOURCE_UUID_NOT_PRESENT);
		}
		
		ResourceEntity re = resourceMgtPersistence.getResource(uuid);
		
		contentMgtService.deleteFileWithUUID(uuid);
		resourceMgtPersistence.deleteResource(uuid);
		
		notificationService.deleteInterestsNotification(re.getId(), EduObjectType.RESOURCE);
	}
	
	private CategoryEntity getCategoryEntity(String categoryName) {
		if (StringUtils.isBlank(categoryName) || StringUtils.isEmpty(categoryName)) {
			logger.warn("Category name not present");
			throw new InvalidArgumentException(CATEGORY_NAME_NOT_PRESENT);
		}
		
		CategoryEntity tce = categoryMgtPersistence.getCategory(categoryName);
		
		if (tce == null) {
			logger.info("Category is not present");
			throw new CategoryException(CATEGORY_NOT_FOUND);
		}
		
		return tce;
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS, rollbackFor = { InvalidArgumentException.class, ResourceException.class })
	public Resource getResource(String uuid) {
		if (StringUtils.isBlank(uuid) || StringUtils.isEmpty(uuid)) {
			logger.warn("Resouce UUID is not present");
			throw new InvalidArgumentException(RESOURCE_UUID_NOT_PRESENT);
		}
		Resource resource = null;
		Content content = contentMgtService.getFileDetailsFromUUID(uuid);

		if (content == null) {
			logger.info("Content is not present for the UUID : " + uuid);
			return resource;
		}

		ResourceEntity re = resourceMgtPersistence.getResource(uuid);
		
		if (re == null) {
			logger.info("Resource with UUID " + uuid + " does not exist");
			throw new ResourceException(RESOURCE_ID_INVALID);
		}
		
		List<ResourceMappingEntity> resourceMappings = resourceMgtPersistence.getParents(re);
		resource = ResourceMgtBeanTransformer.transformResource(re, resourceMappings);
		resource.setInputStream(content.getContentStream());
		
		return resource;
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { InvalidArgumentException.class, ResourceException.class })
	public void mapResource(long resourceId, long objectId, EduObjectType objectType) {
		if (resourceId <= 0) {
			logger.warn("ResourceId is invalid");
			throw new InvalidArgumentException(RESOURCE_ID_INVALID);
		}
		
		if (objectId <= 0) {
			logger.warn("Resource Parent Id is invalid");
			throw new InvalidArgumentException(RESOURCE_PARENT_ID_INVALID);
		}
		
		if (objectType == null) {
			logger.warn("Parent Type is invalid");
			throw new InvalidArgumentException(RESOURCE_PARENT_TYPE_INVALID);
		}
		
		resourceMgtPersistence.mapResource(resourceId, objectId, objectType.toString());
	}

	@Override
	public List<Resource> getResources(long objectId, EduObjectType objectType) {
		if (objectId <= 0) {
			logger.warn("Resource Parent Id is invalid");
			throw new InvalidArgumentException(RESOURCE_PARENT_ID_INVALID);
		}
		
		if (objectType == null) {
			logger.warn("Parent Type is invalid");
			throw new InvalidArgumentException(RESOURCE_PARENT_TYPE_INVALID);
		}
		
		List<ResourceMappingEntity> resourceMappingEntities = resourceMgtPersistence.getResourceMappings(objectId, objectType.toString());
		
		if (resourceMappingEntities == null || resourceMappingEntities.size() == 0) {
			logger.info("There are no resources associated to parentId : " + objectId);
			return null;
		} else {
			return ResourceMgtBeanTransformer.transformResource(resourceMappingEntities);
		}	
		
	}	

}
